﻿package com.bumpslide.net {	import flash.events.Event;	import flash.events.EventDispatcher;	import flash.events.HTTPStatusEvent;	import flash.events.IOErrorEvent;	import flash.events.ProgressEvent;	import flash.events.SecurityErrorEvent;	import flash.events.TimerEvent;	import flash.net.URLLoader;	import flash.net.URLRequest;	import flash.net.URLVariables;	import flash.utils.Timer;		/**	 * This class wraps a URLRequest and provides retry and timeout functionality.	 * 	 * It also implements a basic RPC interface (IRPCRequest) that makes use of the 	 * IResponder interface.  This class also relays URLLoader progress and complete events.	 * 	 * 	 * 	 * 	 * @author David Knape	 */	public class HTTPRequest extends EventDispatcher implements IRPCRequest {
				// Error Messages		public static var ERROR_TIMED_OUT:String = "Request Timed Out";		public static var ERROR_SECURITY:String = "Security Error";		public static var ERROR_DEFAULT:String = "Unknown HTTP Error";		public static var ERROR_IO:String = "I/O Error";		public static var ERROR_CANCELLED:String = "Request Cancelled";				// options		public var debugEnabled:Boolean = false;				// private		protected var _urlLoader:URLLoader;		protected var _urlRequest:URLRequest;		protected var _postData:URLVariables;		protected var _responders:Array;		protected var _loadTimer:Timer;		protected var _timeout:uint = 30;		protected var _retryCount:uint = 3;		protected var _httpStatus:int;		protected var _result:*;		protected var _data:*;				/**		 * Creates a new HTTPRequest that wraps a URLRequest		 */
		public function HTTPRequest( request:URLRequest, responder:IResponder=null ) {			_responders = new Array();				_urlRequest = request;								if(responder!=null) addResponder(responder);		}				/**		 * Adds a responder to the request		 */		public function addResponder( responder:IResponder ):void {			_responders.push(responder);		}   				/**		 * Starts loading the request		 */
		public function load():void {			initTimer();			initRequest();		}				/**		 * Cancels pending request		 */
		public function cancel():void {			killRequest();				killTimer();					raiseError(ERROR_CANCELLED);		}				/**		 * Creates the URLLoader, starts listening to URLLoader events, and loads the URLRequest		 */	
		protected function initRequest():void {			if(_urlLoader != null) killRequest();			_urlLoader = new URLLoader();						_urlLoader.addEventListener("progress", handleProgressEvent);			_urlLoader.addEventListener("ioError", handleIOError);			_urlLoader.addEventListener("securityError", handleSecurityError);   			_urlLoader.addEventListener("complete", handleCompleteEvent);    			_urlLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, handleHttpStatusEvent);			_urlLoader.load(_urlRequest); 		}				/**		 * closes the URLLoader and stops listening to URLLoader events		 */
		protected function killRequest():void {						_urlLoader.removeEventListener("progress", handleProgressEvent);			_urlLoader.removeEventListener("ioError", handleIOError);			_urlLoader.removeEventListener("securityError", handleSecurityError);   			_urlLoader.removeEventListener("complete", handleCompleteEvent);    			_urlLoader.removeEventListener(HTTPStatusEvent.HTTP_STATUS, handleHttpStatusEvent); 			try { 
				_urlLoader.close(); 
			} catch (e:Error) {
			}			}				/**		 * Starts the timer that is used to manage timeouts and retries		 */
		protected function initTimer():void {			_loadTimer = new Timer(timeout * 1000, retryCount);			_loadTimer.addEventListener(TimerEvent.TIMER, handleTimeout);			_loadTimer.addEventListener(TimerEvent.TIMER, handleFinalTimeout);			_loadTimer.start();		}				/**		 * Stops the timer used to manage timeouts and retries		 */
		protected function killTimer():void {			_loadTimer.stop();			_loadTimer.removeEventListener(TimerEvent.TIMER, handleTimeout);			_loadTimer.removeEventListener(TimerEvent.TIMER, handleFinalTimeout);		}
						//-------------------------		// Timer Event Handlers		//-------------------------				protected function handleTimeout(e:TimerEvent = null):void {			debug('retrying');			killRequest();			initRequest();		}
				private function handleFinalTimeout(event:TimerEvent):void {			debug('final try timed out');			killRequest();			raiseError(ERROR_TIMED_OUT);		}
		  		
		//-------------------------		// Loader Event Handlers		//-------------------------				protected function handleIOError(event:IOErrorEvent):void {			raiseError(ERROR_IO);  		}     
				protected function handleSecurityError(event:SecurityErrorEvent):void {			raiseError(ERROR_SECURITY);		}   
				protected function handleHttpStatusEvent(event:HTTPStatusEvent):void {			_httpStatus = event.status;		}
				protected function handleProgressEvent(event:ProgressEvent):void {			dispatchEvent(event);  		}
				protected function handleCompleteEvent(event:Event):void {			killRequest();				killTimer();						debug('Complete');						// save data so event listeners can still reference event.target.data			_data = event.target.data;						// dispatch the complete event			dispatchEvent(event);            			// process the data into some kind of 'result'			processResult(data);            			// pass data to responders			for each (var r:IResponder in _responders) {				if(r.fault != null) r.result(result);			}		}		
		/**		 * This should be overrided by subclasses to parse XML, JSON, etc.		 * 		 * By default, we just set the result to be the raw data		 */		protected function processResult( data:* ):void {			_result = data;		}				/**		 * calls fault handler on responder and traces error message		 */
		protected function raiseError( msg:String = "" ):void {						if(msg == "") msg = ERROR_DEFAULT;						killRequest();				killTimer();						debug('Error: ' + msg);			// pass message to responder fault handlers			for each (var r:IResponder in _responders) {				if(r.fault != null) r.fault(msg);			}		}				/**		 * trace util		 */
		private function debug(msg:*):void {			if(debugEnabled) trace('[HTTPRequest] (' + _urlRequest.url + ') ' + msg);		}				/**		 * Timeout in seconds		 */
		public function get timeout():uint {			return _timeout;		}		
		public function set timeout(seconds:uint):void {			_timeout = seconds;		}				/**		 * Number of times to retry request if it is timing out		 */
		public function get retryCount():uint {			return _retryCount;		}		
		public function set retryCount(n:uint):void {			_retryCount = n;		}				/**		 * The urlRequest we are wrapping		 */
		public function get urlRequest():URLRequest {			return _urlRequest;		}		
		public function set urlRequest(urlRequest:URLRequest):void {			_urlRequest = urlRequest;		}				/**		 * The latest HTTP Status code (ex: 404, 500, etc.)		 */
		public function get httpStatus():int {			return _httpStatus;		}				/**		 * Access to a formatted result object (XML, decoded JSON, etc. - depends on implementation)		 * 		 * This is what gets passed to the responder's result handler		 */
		public function get result():* {			return _result;		}				/**		 * The raw data retrieved from the server		 */
		public function get data():* {			return _data;		}	}}